<?php

namespace App\Service\Common;

use App\Enums\BaseErrorCode;
use App\Enums\OpenShop\InvestmentPromotion\SiteLocationEnum;
use App\Exceptions\Custom\ResponseHttpException;
use App\Exceptions\KfHttpResponseException;
use App\Http\Middleware\OpenShop\WangGuiCityByPower;
use App\Library\FeiShu\SendFs;
use App\Models\Common\CommonPushBindModel;
use App\Models\Common\ConfigModel;
use App\Models\Common\HolidayModel;
use App\Models\Recognize\RecognizeFoodProduceLicenseResult;
use App\Remote\Rms\RmsShopRemote;
use App\Service\OpenShop\Feishu\BaseService;
use Carbon\Carbon;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Kuafu\File\FileUtil;
use Kuafu\File\PrivateFileUtil;
use Kuafu\GeneralFs\BuildFs;
use Kuafu\Recognizer\BankCardRecognize;
use Kuafu\Recognizer\FoodProduceLicenseRecognize;
use Kuafu\Recognizer\Recognizer;
use Kuafu\Recognizer\RecognizeSocialSecurityCard;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class CommonService extends BaseService
{
    /**
     * @param UploadedFile $uploadedFile
     * @return array|void
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws Exception
     */
    public static function privateUpload(UploadedFile $uploadedFile)
    {
        try {
            $suffix = FileUtil::guessSuffix($uploadedFile->getClientOriginalName());
            return PrivateFileUtil::uploadFile(
                $uploadedFile->getRealPath(),
                $suffix,
                generateFileKey() . $suffix
            );
        } catch (Exception $e) {
            handleException($e);
        }
    }

    /**
     * 食品许可证识别
     *
     * @param UploadedFile $uploadedFile
     * @return array|void
     * @throws Exception
     */
    public static function foodProduceLicenseRecognize(UploadedFile $uploadedFile, string $expect = '')
    {
        try {
            $suffix = FileUtil::guessSuffix($uploadedFile->getClientOriginalName());
            $upload = OssService::uploadFile($uploadedFile->getRealPath(), $suffix);

            $recognizer = new Recognizer(FoodProduceLicenseRecognize::getInstance());
            $recognize  = $recognizer->recognizer($uploadedFile->getRealPath());
            $jsonRecognize = str_replace("'",'',json_encode($recognize,JSON_UNESCAPED_UNICODE));
            $recognize = json_decode($jsonRecognize,1);
            if (isset($recognize["codes"])) {
                unset($recognize["codes"]);
            }
            if (isset($upload['path']) && isset($recognize['data'])) {
                RecognizeFoodProduceLicenseResult::add($upload['path'], $recognize['data']);//存下识别结果
            }
            return [
                "upload"    => $upload,
                "recognize" => $recognize
            ];
        } catch (Exception $exception) {
            if ($expect == 'ignoreOcrError'){
                if (!isset($upload)){
                    self::throwErr('上传失败', \App\Enums\OpenShop\BaseErrorCode::BASE_CODE_UPDATE_FAIL);
                }
                return [
                    "upload"    => $upload,
                    "recognize" => []
                ];
            }

            if(str_contains($exception->getMessage(), "unmatchedImageType")){
                handleException((new ResponseHttpException(BaseErrorCode::$ERROR_RECOGNIZE_FOOD_PRODUCT_LICENSE, null, "食品经营许可证识别错误")));
            }else{
                handleException($exception, BaseErrorCode::$ERROR_RECOGNIZE_FOOD_PRODUCT_LICENSE, null, "食品经营许可证识别错误");
            }
        }
    }

    /**
     * 银行卡识别
     *
     * @param UploadedFile $uploadedFile
     * @return array|void
     * @throws Exception
     */
    public static function recognizeBankCard(UploadedFile $uploadedFile)
    {
        $suffix = FileUtil::guessSuffix($uploadedFile->getClientOriginalName());
        try {
            $recognizer = new Recognizer(BankCardRecognize::getInstance());
            $recognize  = $recognizer->recognizer($uploadedFile->getRealPath());
            if (isset($recognize["codes"])) {
                unset($recognize["codes"]);
            }
            return [
                "upload"    => OssService::uploadFile($uploadedFile->getRealPath(), $suffix),
                "recognize" => $recognize
            ];
        } catch (Exception $exception) {
            $res['recognize']['data']['bankName']    = '';
            $res['recognize']['data']['bankAccount'] = '';
            $res['recognize']['data']['validToDate'] = '';
            $res['recognize']['data']['cardType']    = '';
            if ($exception->getCode() == 400 && strpos($exception->getMessage(), 'unmatchedImageType') !== false) {// 未匹配的图像类型

                // 带有银联的社保卡识别
                try {
                    $res = self::recognizeSocialSecurityCard($uploadedFile);
                    if (isset($res['recognize']['data'])) {                                                      // 银行名称
                        $res['recognize']['data']['cardNumber'] = $res['recognize']['data']['bankAccount'] ?? '';// 银行卡号
                    }
                    return $res;
                } catch (Exception $e) {

                }
            }

            // 银行卡识别失败不再报错，返回图片，用户手动填写卡号，银行等信息
            return [
                'upload'    => OssService::uploadFile($uploadedFile->getRealPath(), $suffix),
                'recognize' => $res
            ];
//            handleException($exception, BaseErrorCode::$ERROR_RECOGNIZE_BANK_CARD);
        }
    }

    public static function getWeatherByAdCode($adcode)
    {
        try {
            $key             = WangGuiCityByPower::getRandomAmapKey();
            $urlWeather      = "https://restapi.amap.com/v3/weather/weatherInfo?city={$adcode}&key={$key}";
            $responseWeather = (new Client(['http_errors' => false]))
                ->get($urlWeather);
            $bodyWeather     = $responseWeather->getBody()->getContents();
            return json_decode($bodyWeather, true);
        } catch (\Exception $exception) {
            \App\Service\BaseService::throwErr('获取天气数据失败');
        }
    }

    /**
     * 金额 单位处理
     * @param int $number
     * @return string
     */
    public static function floatNumber(int $number): string
    {
        if ($number > 0) {
            return bcdiv($number, 10000, 1);//（单位：W）
        }
        return "0";
        /*$length = strlen($number);  //数字长度
        if ($length > 8) { //亿单位
            $str = substr_replace(strstr($number, substr($number, -7), ' '), '.', -1, 0) . "亿";
        } elseif ($length > 4) { //万单位
            //截取前俩为
            $str = substr_replace(strstr($number, substr($number, -3), ' '), '.', -1, 0);// . "万"
        } else {
            return $number;
        }
        return $str;*/
    }

    /**
     * @param object $params
     * @throws ValidationException
     */
    public static function devicePushBind(object $params)
    {
        try {
            $sign = md5(vsprintf("mobileBrand:%s|regId:%s", [
                $params->mobileBrand,
                $params->regId
            ]));

            CommonPushBindModel::query()
                               ->where(function ($query) use ($sign) {
                                   $query->where("sign", $sign)->orWhere("rmsUserId", request()->user()->id);
                               })
                               ->update([
                                   "status"    => CommonPushBindModel::STATUS_OFF,
                                   "updatedAt" => time()
                               ]);

            if ($params->mobileBrand > 0) {
                CommonPushBindModel::query()->updateOrCreate(
                    [
                        "rmsUserId"   => request()->user()->id,
                        "mobileBrand" => $params->mobileBrand,
                    ],
                    [
                        "status" => CommonPushBindModel::STATUS_ON,
                        "regId"  => $params->regId,
                        "sign"   => $sign
                    ]
                );
            }

        } catch (Exception $exception) {
            handleException($exception, null, null, "设备推送配置绑定失败");
        }
    }


    /**
     * 快递查询接口
     * @param string $company
     * @param string $number
     * @param string $sign
     * @return array
     */
    public static function logisticsQuery(string $company, string $number, string $sign): array
    {
        $data = [
            'no'   => $number,
            'type' => $company,
            'sign' => $sign,
        ];
        return Cache::remember("logisticsQuery:" . $sign, getSwitchCacheTime(1800),
            function () use ($data) {
                try {
                    list($httpCode, $body, $msg) = (new \Kuafu\express\KuafuExpress())->query($data);
                    Log::info(
                        'logisticsQuery:', [
                            'data'     => $data,
                            'httpCode' => $httpCode,
                            'body'     => $body,
                            'msg'      => $msg,
                        ]
                    );
                    $res = json_decode($body ?? "", true);
                } catch (\Exception $exception) {
                    Log::error(
                        'logisticsQuery Err:', [
                            'data' => $data,
                            'msg'  => $exception->getMessage(),
                        ]
                    );
                    $res = [];
                }
                return $res;
            }
        );
    }


    /**
     * 按天获取工作日期
     * @param $date
     * @param int $days
     * @return bool[]
     */
    public static function getWorkDateByDays(int $days, $date = ''): array
    {
        if (!$date) {
            $date = date('Ymd');
        }

        $count = 0;

        while ($count < abs($days)) {
            if ($days >= 0) {
                $date = Carbon::createFromFormat('Ymd', $date)->addDay()->format('Ymd');
            } else {
                $date = Carbon::createFromFormat('Ymd', $date)->subDay()->format('Ymd');
            }
            if (HolidayModel::checkWork($date)) {
                $count++;
            }
        }

        return [
            'timestamp' => Carbon::createFromFormat('Ymd', $date)->timestamp,
        ];
    }

    public static function getAmapWebJsApi($errorKey)
    {
        $configModel = ConfigModel::query()
            ->where('module', 'amap')
            ->where('name', 'webJs')
            ->first();

        if (null == $configModel) {
            throw new ResponseHttpException(404, null, '配置不存在');
        }

        $config = json_decode($configModel->value, true);

        // 一个小时内提醒一次
        if (count($config['normal']) <= 3 && !Cache::get('amapWebJsApiKeyLackRemind')) {
            Cache::remember('amapWebJsApiKeyLackRemind', 3600, function () {
                return true;
            });
            BuildFs::newRobot(SendFs::ROBOT_OPEN_SHOP)
                ->setTitleContent("检测到高德web端(JS API)的key的正常数量已经小于4个了，请注意", '')
                ->setAtUid('2c54915f') // 胡迪
                ->setAtUid('e5feb9cc') // 镜悬
                ->setAtUid('513296d9') // 彼得潘
                ->send();
        }

        if (strlen($errorKey)) {
            // 获得错误的
            $wrong = [];
            foreach ($config['normal'] as $key => $item) {
                // 判断是否是失效的key
                if ($item['key'] == $errorKey) {
                    $wrong[] = $item;
                    unset($config['normal'][$key]);
                }
            }

            $config['error']  = array_merge($config['error'], $wrong);
            $config['normal'] = array_values($config['normal']);

            $configModel->value = json_encode($config, JSON_UNESCAPED_UNICODE);
            $configModel->save();
        }

        if (!count($config['normal'])) {
            $randomArr = $config['special'];
        } else {
            $randomArr = $config['normal'];
        }

        return Arr::random($randomArr, 1);
    }


    /**
     * 查询门店属性配置项
     * @param string $field
     * @return array
     */
    public static function getShopAttributeData(string $field = ''): array
    {
        $res = RmsShopRemote::getShopAttributeData() ?? [];
        if ($field && $res) {
            foreach ($res as &$v) {
                if ($field == 'shopType' && $v['id'] == SiteLocationEnum::CATEGORY_ID_2) {
                    foreach ($v['value'] as $k => &$value) {// 选址阶段返回 标准店 精选店 团餐店 其他类型不可选择
                        if (!(strpos($value['name'], '标准店') !== false ||
                            strpos($value['name'], '精选店') !== false ||
                            strpos($value['name'], '团餐店') !== false)) {
                            unset($v['value'][$k]);
                        }
                    }
                    $v['value'] = array_values($v['value']);
                    break;
                }
            }
        }
        return $res;
    }

    /**
     * 社保卡识别
     *
     * @param UploadedFile $uploadedFile
     * @return array|void
     * @throws Exception
     */
    public static function recognizeSocialSecurityCard(UploadedFile $uploadedFile): array
    {
        try {
            $recognizer = new Recognizer(RecognizeSocialSecurityCard::getInstance());
            $suffix     = FileUtil::guessSuffix($uploadedFile->getClientOriginalName());
            $recognize  = $recognizer->recognizer($uploadedFile->getRealPath());
            if (isset($recognize['codes'])) {
                unset($recognize['codes']);
            }
            return [
                'upload'    => OssService::uploadFile($uploadedFile->getRealPath(), $suffix),
                'recognize' => $recognize
            ];
        } catch (Exception $exception) {
            handleException($exception, BaseErrorCode::$ERROR_RECOGNIZE_SOCIAL_SECURITY_CARD);
        }
    }

    /**
     * 全部的消息卡片，涉及到加盟商信息的地方，我们都调整下显示逻辑，加星号处理 刘仁杰2892 13361868635 调整为： 刘**  133****8635
     * @param string $field
     * @param string $type
     * @param bool $isBackslash 如果你希望在消息卡片中显示**而不是被解析为Markdown语法，你可以使用反斜杠进行转义，比如写成`\\**`。这样就可以正常显示**，而不会被解析为Markdown语法
     * @return string
     */
    public static function getUserInfoHide(string $field, string $type, bool $isBackslash = false): string
    {
        switch ($type) {
            case "name": // 姓名
                if ($isBackslash) {
                    return mb_substr($field, 0, 1, 'UTF-8') . '\\\\*\\\\*';
                } else {
                    return mb_substr($field, 0, 1, 'UTF-8') . '**';
                }
            case 'mobile': // 手机号
                if ($isBackslash) {
                    return substr_replace($field, '\\\\*\\\\*\\\\*\\\\*', 3, 4);
                } else {
                    return substr_replace($field, '****', 3, 4);
                }
            default:
                return $field;
        }
    }
}
